from pymongo import MongoClient
import numpy as np

from PeriodFactorGeneralFunctions import PeriodFactorGeneralFunctions

from dateutil.relativedelta import relativedelta

from datetime import datetime
import time

class PeriodFactors:

    def __init__(self):
        periodFactorFuncObj = PeriodFactorGeneralFunctions()
        self.fillType = periodFactorFuncObj.fillType

        self.DBIP = periodFactorFuncObj.DBIP
        self.DBPort = periodFactorFuncObj.DBPort

        self.BasisDBName = periodFactorFuncObj.DBName


        self.ClosePeriodBasisKeyName = 'all_period_close_'



    def WriteUpdateFixedPeriodFactor(self, colTransDatesName, colKeyName, colPeriodKeyNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        BasisDBName = self.BasisDBName

        client = MongoClient(DBIP, DBPort)
        dbBasis = client[BasisDBName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        dbFactors = client[FactorDBName]

        colCombinedName = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum)

        collection_daily_factor_input = dbBasis[colKeyName]
        collection_period_factor_ouput = dbFactors[colCombinedName]

        strUpdateKey = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum) + '_update_date'

        try:
            strUpdateDate = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            updateDate = datetime.strptime(strUpdateDate, '%Y-%m-%d')
            strUpdateDate = updateDate.strftime('%Y%m%d')
        except:
            strUpdateDate = ''

        if strUpdateDate != '':
            startDateStr = strUpdateDate

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')
            for periodFactorRes in collection_daily_factor_input.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodFactorRes['StockID']
                try:
                    floatFactorValue = float(periodFactorRes[strDate])
                except KeyError:
                    floatFactorValue = 0.0
                    print("key error")
                    #next
                if (np.isnan(floatFactorValue) or np.isinf(floatFactorValue)):
                    floatFactorValue = 0.0
                collection_period_factor_ouput.update({'StockID': strStockID}, {'$set': {strDate: floatFactorValue}}, upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')


        strKeyAlias = ' monthly close data with offset of ' + str(offsetNum) + ' days '
        endDate = datetime.strptime(endDateStr, '%Y%m%d')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, strKeyAlias, endDate.strftime('%Y-%m-%d'))

    def WriteUpdateMomentPeriodFactorBak(self, colTransDatesName, colKeyName, colPeriodKeyNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum, mmtDiffNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        BasisDBName = self.BasisDBName

        client = MongoClient(DBIP, DBPort)
        dbBasis = client[BasisDBName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        dbFactors = client[FactorDBName]

        colCombinedName = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum)

        collection_daily_factor_input = dbBasis[colKeyName]
        collection_period_factor_ouput = dbFactors[colCombinedName]

        strUpdateKey = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum) + '_update_date'

        try:
            strUpdateDate = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            updateDate = datetime.strptime(strUpdateDate, '%Y-%m-%d')
            strUpdateDate = updateDate.strftime('%Y%m%d')
        except:
            strUpdateDate = ''

        if strUpdateDate != '':
            startDateStr = strUpdateDate

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')
            for periodFactorRes in collection_daily_factor_input.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodFactorRes['StockID']
                try:
                    floatFactorValue = float(periodFactorRes[strDate])
                except KeyError:
                    floatFactorValue = 0.0
                    print("key error")
                    #next
                if (np.isnan(floatFactorValue) or np.isinf(floatFactorValue)):
                    floatFactorValue = 0.0
                collection_period_factor_ouput.update({'StockID': strStockID}, {'$set': {strDate: floatFactorValue}}, upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')


        strKeyAlias = ' monthly close data with offset of ' + str(offsetNum) + ' days '
        endDate = datetime.strptime(endDateStr, '%Y%m%d')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, strKeyAlias, endDate.strftime('%Y-%m-%d'))


    def WriteUpdateCumulativePeriodFactor(self, colTransDatesName, colKeyName, colPeriodKeyNameMain, colNameAlias, periodTypeStr,
                                 startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        BasisDBName = self.BasisDBName

        client = MongoClient(DBIP, DBPort)
        dbBasis = client[BasisDBName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        dbFactors = client[FactorDBName]

        colCombinedName = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_daily_factor_input = dbBasis[colKeyName]
        collection_period_factor_ouput = dbFactors[colCombinedName]

        strUpdateKey = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum) + '_update_date'

        try:
            strUpdateDate = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            updateDate = datetime.strptime(strUpdateDate, '%Y-%m-%d')
            strUpdateDate = updateDate.strftime('%Y%m%d')
        except:
            strUpdateDate = ''

        if strUpdateDate != '':
            startDateStr = strUpdateDate


        # allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        noOffsetAllPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr,
                                                                                       startDateStr, endDateStr, 0)
        for periodDateItem in noOffsetAllPeriodDateList:
            strPointDate = periodDateItem.strftime('%Y%m%d')

            # we need to get all dates ended in the period defined by strDate, i.e., to retrace all dates from this day to the previous period end
            # offsetNum will be used here only
            selectedDateList = objPeriodFactorGenFunc.RetrievePeriodDateGroupList(colTransDatesName, periodTypeStr,
                                                                                  strPointDate, offsetNum)

            searchCommondDict = {}
            searchCommondDict['StockID'] = 1
            for dateItem in selectedDateList:
                searchCommondDict[dateItem] = 1

            for periodFactorRes in collection_daily_factor_input.find({}, searchCommondDict):
                tempFactorValue = 0.0
                strStockID = periodFactorRes['StockID']

                for dateItem in selectedDateList:

                    try:
                        floatFactorValue = float(periodFactorRes[dateItem])
                    except:
                        floatFactorValue = 0.0
                    if (np.isnan(floatFactorValue) or np.isinf(floatFactorValue)):
                        floatFactorValue = 0.0
                    tempFactorValue = tempFactorValue + floatFactorValue

                collection_period_factor_ouput.update({'StockID': strStockID}, {'$set': {strPointDate: tempFactorValue}},upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strPointDate + ' has been updated to database ')
        strKeyAlias = ' monthly ' + colKeyName + ' data with offset of ' + str(offsetNum) + ' days '
        endDate = datetime.strptime(endDateStr, '%Y%m%d')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, strKeyAlias, endDate.strftime('%Y-%m-%d'))



    def WriteUpdateReciprocalPeriodFactor(self, colTransDatesName, colKeyName, colPeriodKeyNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        BasisDBName = self.BasisDBName

        client = MongoClient(DBIP, DBPort)
        dbBasis = client[BasisDBName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        dbFactors = client[FactorDBName]

        colCombinedName = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum)

        collection_daily_factor_input = dbBasis[colKeyName]
        collection_period_factor_ouput = dbFactors[colCombinedName]

        strUpdateKey = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum) + '_update_date'

        try:
            strUpdateDate = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            updateDate = datetime.strptime(strUpdateDate, '%Y-%m-%d')
            strUpdateDate = updateDate.strftime('%Y%m%d')
        except:
            strUpdateDate = ''

        if strUpdateDate != '':
            startDateStr = strUpdateDate

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')
            for periodFactorRes in collection_daily_factor_input.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodFactorRes['StockID']
                try:
                    floatFactorValue = float(periodFactorRes[strDate])
                    if (np.isnan(floatFactorValue) or np.isinf(floatFactorValue)):
                        floatFactorValue = 0.0
                    if floatFactorValue != 0.0:
                        floatFactorValue = 1 / floatFactorValue
                except KeyError:
                    floatFactorValue = 0.0
                    next

                collection_period_factor_ouput.update({'StockID': strStockID}, {'$set': {strDate: floatFactorValue}}, upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')


        strKeyAlias = ' monthly close data with offset of ' + str(offsetNum) + ' days '
        endDate = datetime.strptime(endDateStr, '%Y%m%d')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, strKeyAlias, endDate.strftime('%Y-%m-%d'))


    # def WriteUpdateCumulativePeriodFactor_bak(self, colTransDatesName, colKeyName, colPeriodKeyNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
    #     DBIP = self.DBIP
    #     DBPort = self.DBPort
    #     BasisDBName = self.BasisDBName
    #
    #     client = MongoClient(DBIP, DBPort)
    #     dbBasis = client[BasisDBName]
    #
    #     objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
    #     FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)
    #
    #     dbFactors = client[FactorDBName]
    #
    #     colCombinedName = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
    #
    #     collection_daily_factor_input = dbBasis[colKeyName]
    #     collection_period_factor_ouput = dbFactors[colCombinedName]
    #
    #     strUpdateKey = colPeriodKeyNameMain + '_' + periodTypeStr + '_' + str(offsetNum) + '_update_date'
    #
    #     try:
    #         strUpdateDate = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
    #         updateDate = datetime.strptime(strUpdateDate, '%Y-%m-%d')
    #         strUpdateDate = updateDate.strftime('%Y%m%d')
    #     except:
    #         strUpdateDate = ''
    #
    #     if strUpdateDate != '':
    #         startDateStr = strUpdateDate
    #
    #     allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)
    #
    #     for periodDateItem in allPeriodDateList:
    #         strDate = periodDateItem.strftime('%Y%m%d')
    #         for periodFactorRes in collection_daily_factor_input.find({}, {'StockID': 1, strDate: 1}):
    #             strStockID = periodFactorRes['StockID']
    #             try:
    #                 floatFactorValue = float(periodFactorRes[strDate])
    #             except KeyError:
    #                 floatFactorValue = 0.0
    #                 next
    #
    #             collection_period_factor_ouput.update({'StockID': strStockID}, {'$set': {strDate: floatFactorValue}}, upsert=True)
    #             print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')
    #
    #
    #     strKeyAlias = ' monthly close data with offset of ' + str(offsetNum) + ' days '
    #     endDate = datetime.strptime(endDateStr, '%Y%m%d')
    #     objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, strKeyAlias, endDate.strftime('%Y-%m-%d'))





    def WriteUpdateMomentPeriodFactor(self,colPeriodCloseNameMain, colPeriodMomentNameMain, colNameAlias, periodTypeStr, offsetNum, mmtDiffNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        client = MongoClient(DBIP, DBPort)

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        db = client[FactorDBName]
        colCloseCombinedName = colPeriodCloseNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        coMomentCombinedName = colPeriodMomentNameMain + str(mmtDiffNum) + periodTypeStr + '_mmt_' + periodTypeStr + '_'+ str(offsetNum)
        collection_period_close = db[colCloseCombinedName]
        collection_period_moment = db[coMomentCombinedName]

        strUpdateKey = coMomentCombinedName + '_update_date'

        try:
            strUpdateNum = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            factorStartPeriodNum = int(strUpdateNum)
            if factorStartPeriodNum < 0:
                factorStartPeriodNum = 0
        except:
            factorStartPeriodNum = 0

        maxDateNum = 0
        for onePeriodStockID in collection_period_close.find({}, {'StockID': 1}):
            strStockID = onePeriodStockID['StockID']

            for onePeriodCloseItem in collection_period_close.find({'StockID': strStockID}):
                aDateStrList = []
                aCloseNumberList = []
                for (k, v) in onePeriodCloseItem.items():
                    try:
                        datetime.strptime(str(k), "%Y%m%d")
                        aDateStrList.append(str(k))
                        aCloseNumberList.append(float(v))
                    except:
                        next
                dateNumber = len(aDateStrList)
                if dateNumber > maxDateNum:
                    maxDateNum = dateNumber
                oneStockMomentDict = {}
                oneStockMomentDict['StockID'] = strStockID
                for j in range(0, mmtDiffNum):
                    collection_period_moment.update({'StockID': strStockID}, {'$set': {aDateStrList[j]: 0.0}}, upsert=True)

                for i in range(factorStartPeriodNum + mmtDiffNum, dateNumber):
                    try:
                        aPeriodNum = aCloseNumberList[i] / aCloseNumberList[i-mmtDiffNum]
                    except ZeroDivisionError:
                        aPeriodNum = 0.0
                    collection_period_moment.update({'StockID': strStockID}, {'$set': {aDateStrList[i]: aPeriodNum}}, upsert=True)

            print('Period ' + str(mmtDiffNum) + ' ' + periodTypeStr + ' ' + colNameAlias + ' data of ' +strStockID + ' has been updated to database ')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, ' ' + strUpdateKey + ' ', str(maxDateNum - mmtDiffNum -1))


    def WriteUpdateYieldPeriodFactor(self,colPeriodCloseNameMain, colPeriodYieldNameMain, colNameAlias, periodTypeStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        client = MongoClient(DBIP, DBPort)

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()
        FactorDBName = objPeriodFactorGenFunc.ObtainOutputDBName(periodTypeStr)

        db = client[FactorDBName]
        colCloseCombinedName = colPeriodCloseNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        coYieldCombinedName = colPeriodYieldNameMain + colNameAlias + '_' + periodTypeStr + '_'+ str(offsetNum)
        collection_period_close = db[colCloseCombinedName]
        collection_period_yield = db[coYieldCombinedName]

        strUpdateKey = coYieldCombinedName + '_update_date'

        try:
            strUpdateNum = objPeriodFactorGenFunc.retrieveDataUpdateDate(FactorDBName, strUpdateKey)
            factorStartPeriodNum = int(strUpdateNum)
            if factorStartPeriodNum < 0:
                factorStartPeriodNum = 0
        except:
            factorStartPeriodNum = 0

        maxDateNum = 0
        for onePeriodStockID in collection_period_close.find({}, {'StockID': 1}):
            strStockID = onePeriodStockID['StockID']

            for onePeriodCloseItem in collection_period_close.find({'StockID': strStockID}):
                aDateStrList = []
                aCloseNumberList = []
                for (k, v) in onePeriodCloseItem.items():
                    try:
                        datetime.strptime(str(k), "%Y%m%d")
                        aDateStrList.append(str(k))
                        aCloseNumberList.append(float(v))
                    except:
                        next
                dateNumber = len(aDateStrList)
                if dateNumber > maxDateNum:
                    maxDateNum = dateNumber
                #oneStockMomentDict = {}
                #oneStockMomentDict['StockID'] = strStockID

                #collection_period_yield.update({'StockID': strStockID}, {'$set': {aDateStrList[0]: 0.0}}, upsert=True)

                for i in range(factorStartPeriodNum, dateNumber-1):
                    try:
                        aPeriodNum = aCloseNumberList[i+1] / aCloseNumberList[i]
                    except ZeroDivisionError:
                        aPeriodNum = 0.0
                    collection_period_yield.update({'StockID': strStockID}, {'$set': {aDateStrList[i]: aPeriodNum}}, upsert=True)
                collection_period_yield.update({'StockID': strStockID}, {'$set': {aDateStrList[dateNumber-1]: float('NaN')}}, upsert=True)
            print('Period ' + colNameAlias + ' data of ' +strStockID + ' has been updated to database ')
        objPeriodFactorGenFunc.updateDataUpdateDate(FactorDBName, strUpdateKey, ' ' + strUpdateKey + ' ', str(maxDateNum-2))


    def WriteUpdatePeriodRelativeMoment(self, colPeriodCloseNameMain, colPeriodMomentdNameMain, colNameAlias, periodTypeStr, mmtRelativeDaydNum, offsetNum, factorStartPeriodNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        DBName = self.DBName
        client = MongoClient(DBIP, DBPort)
        db = client[DBName]
        colCloseCombinedName = colPeriodCloseNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        colYieldCombinedName = colPeriodMomentdNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_period_close = db[colCloseCombinedName]
        collection_period_yield = db[colYieldCombinedName]

        for onePeriodStockID in collection_period_close.find({}, {'StockID': 1}):
            strStockID = onePeriodStockID['StockID']

            for onePeriodCloseItem in collection_period_close.find({'StockID': strStockID}):
                aDateStrList = []
                aCloseNumberList = []
                for (k, v) in onePeriodCloseItem.items():
                    try:
                        datetime.datetime.strptime(str(k), "%Y%m%d")
                        aDateStrList.append(str(k))
                        aCloseNumberList.append(float(v))
                    except:
                        next
                dateNumber = len(aDateStrList)
                oneStockYieldDict = {}
                oneStockYieldDict['StockID'] = strStockID
                for i in range(factorStartPeriodNum, dateNumber - 1):
                    try:
                        aPeriodNum = aCloseNumberList[i + 1] / aCloseNumberList[i]
                    except ZeroDivisionError:
                        aPeriodNum = 0

                    collection_period_yield.update({'StockID': strStockID},
                                                       {'$set': {aDateStrList[i]: str(aPeriodNum)}}, upsert=True)
                collection_period_yield.update({'StockID': strStockID},
                                                   {'$set': {aDateStrList[dateNumber - 1]: '-1'}}, upsert=True)
            print('Period ' + colNameAlias + ' data of ' + strStockID + ' has been updated to database ')



    def WriteUpdatePeriodMktFreeShares(self, colTransDatesName, colDailyMktFreeSharesName, colPeriodMktFreeSharesNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        DBName = self.DBName
        client = MongoClient(DBIP, DBPort)
        db = client[DBName]
        colCombinedName = colPeriodMktFreeSharesNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_period_close = db[colCombinedName]
        collection_daily_mkt_freeshares = db[colDailyMktFreeSharesName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')

            for periodRes in collection_daily_mkt_freeshares.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodRes['StockID']
                try:
                    strClose = periodRes[strDate]
                except KeyError:
                    strClose = '0'
                    next
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')
                collection_period_close.update({'StockID': strStockID}, {'$set': {strDate: strClose}}, upsert=True)


    def WriteUpdatePeriodEV(self, colTransDatesName, colDailydReciprocalPeName, colPeriodReciprocalPeNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        DBName = self.DBName
        client = MongoClient(DBIP, DBPort)
        db = client[DBName]
        colCombinedName = colPeriodReciprocalPeNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_period_reciprocal_pe = db[colCombinedName]
        collection_daily_reciprocal_pe = db[colDailydReciprocalPeName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')

            for periodRes in collection_daily_reciprocal_pe.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodRes['StockID']
                try:
                    strReciprocalPe = periodRes[strDate]
                except KeyError:
                    strReciprocalPe = '0'
                    next
                collection_period_reciprocal_pe.update({'StockID': strStockID}, {'$set': {strDate: strReciprocalPe}}, upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')

    def WriteUpdatePerioReciprocalPE(self, colTransDatesName, colDailyEvName, colPeriodEvNameMain, colNameAlias, periodTypeStr, startDateStr, endDateStr, offsetNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        DBName = self.DBName
        client = MongoClient(DBIP, DBPort)
        db = client[DBName]
        colCombinedName = colPeriodEvNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_period_close = db[colCombinedName]
        collection_daily_ev = db[colDailyEvName]

        objPeriodFactorGenFunc = PeriodFactorGeneralFunctions()

        allPeriodDateList = objPeriodFactorGenFunc.RetrievePeriodPointDateList(colTransDatesName, periodTypeStr, startDateStr, endDateStr, offsetNum)

        for periodDateItem in allPeriodDateList:
            strDate = periodDateItem.strftime('%Y%m%d')

            for periodRes in collection_daily_ev.find({}, {'StockID': 1, strDate: 1}):
                strStockID = periodRes['StockID']
                try:
                    strClose = periodRes[strDate]
                except KeyError:
                    strClose = '0'
                    next
                collection_period_close.update({'StockID': strStockID}, {'$set': {strDate: strClose}}, upsert=True)
                print('Period ' + colNameAlias + ' data of ' + str(strStockID) + ' at ' + strDate + ' has been updated to database ')

    def WriteUpdatePeriodYieldBak(self, colPeriodCloseNameMain, colPeriodYieldNameMain, colNameAlias, periodTypeStr, offsetNum, factorStartPeriodNum):
        DBIP = self.DBIP
        DBPort = self.DBPort
        DBName = self.DBName
        client = MongoClient(DBIP, DBPort)
        db = client[DBName]
        colCloseCombinedName = colPeriodCloseNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        colYieldCombinedName = colPeriodYieldNameMain + '_' + periodTypeStr + '_' + str(offsetNum)
        collection_period_close = db[colCloseCombinedName]
        collection_period_yield = db[colYieldCombinedName]

        for onePeriodStockID in collection_period_close.find({}, {'StockID': 1}):
            strStockID = onePeriodStockID['StockID']

            for onePeriodCloseItem in collection_period_close.find({'StockID': strStockID}):
                aDateStrList = []
                aCloseNumberList = []
                for (k, v) in onePeriodCloseItem.items():
                    try:
                        datetime.datetime.strptime(str(k), "%Y%m%d")
                        aDateStrList.append(str(k))
                        aCloseNumberList.append(float(v))
                    except:
                        next
                dateNumber = len(aDateStrList)
                oneStockYieldDict = {}
                oneStockYieldDict['StockID'] = strStockID
                for i in range(factorStartPeriodNum, dateNumber-1):
                    try:
                        aPeriodNum = aCloseNumberList[i+1] / aCloseNumberList[i]
                    except ZeroDivisionError:
                        aPeriodNum = 0
                    collection_period_yield.update({'StockID': strStockID}, {'$set': {aDateStrList[i]: str(aPeriodNum)}}, upsert=True)
                collection_period_yield.update({'StockID': strStockID}, {'$set': {aDateStrList[dateNumber-1]: 'N/A'}}, upsert=True)
            print('Period ' + colNameAlias + ' data of ' +strStockID + ' has been updated to database ')



periodFuncsObj = PeriodFactors()

initialDayStr = '20120101'

baseDayStr = '20170501'

currDayStr = time.strftime('%Y%m%d')

#periodFuncsObj.WriteUpdateFixedPeriodFactor('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateReciprocalPeriodFactor('trans_dates', 'pe_ttm', 'all_period_reciprocal_pe_ttm', 'all period reciprocal of pe_ttm', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateFixedPeriodFactor('trans_dates', 'ev', 'all_period_ev', 'all period ev', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateFixedPeriodFactor('trans_dates', 'mkt_freeshares', 'all_period_mkt_freeshares', 'all period mkt_freeshares', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateCumulativePeriodFactor('trans_dates', 'mfd_buyamt_d4', 'all_period_mfd_buyamt_d4', 'mfd buy amt d4', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateCumulativePeriodFactor('trans_dates', 'mfd_buyamt_d1', 'all_period_mfd_buyamt_d1', 'mfd buy amt d1', 'M', initialDayStr, currDayStr, 0)

periodFuncsObj.WriteUpdateMomentPeriodFactor('all_period_close', 'all_period_', 'moment', 'M', 0, 1)

#periodFuncsObj.WriteUpdateMomentPeriodFactor('all_period_close', 'all_period_', 'moment', 'M', 0, 3)

periodFuncsObj.WriteUpdateYieldPeriodFactor('all_period_close', 'all_period_', 'yield', 'M', 0)

periodFuncsObj.WriteUpdateFixedPeriodFactor('trans_dates', 'ev', 'all_period_ev', 'all period ev', 'M', initialDayStr, currDayStr, 1)




#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', initialDayStr, baseDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', initialDayStr, baseDayStr, -1)

#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', initialDayStr, baseDayStr, 1)

#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', baseDayStr, currDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'all period close', 'M', baseDayStr, currDayStr, -1)

#periodFuncsObj.WriteUpdatePeriodClose('trans_dates', 'daily_close', 'all_period_close', 'close', 'M', baseDayStr, currDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodYield('all_period_close', 'all_period_yield', 'yield', 'M', 0, 0)

# the number 61 is an approximate value for finding the date around 201705
#periodFuncsObj.WriteUpdatePeriodYield('all_period_close', 'all_period_yield', 'yield', 'M', 0, 0)



#periodFuncsObj.WriteUpdatePeriodMktFreeShares('trans_dates', 'mkt_freeshares', 'all_period_mkt_freeshares', 'all period market freeshares', 'M', baseDayStr, currDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodEV('trans_dates', 'ev', 'all_period_ev', 'all period ev', 'M', baseDayStr, currDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodBuySell('trans_dates', 'mfd_buyamt_d4', 'all_period_mfd_buyamt_d4', 'mfd buy amt d4', 'M', initialDayStr, baseDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodBuySell('trans_dates', 'mfd_buyamt_d4', 'all_period_mfd_buyamt_d4', 'mfd buy amt d4', 'M', baseDayStr, currDayStr, 0)

#periodFuncsObj.WriteUpdatePeriodBuySell('trans_dates', 'reciprocal_pe_ttm', 'all_reciprocal_pe_ttm', 'Reciprocal of PE_ttm', 'M', initialDayStr, baseDayStr, 0)
#periodFuncsObj.WriteUpdatePeriodBuySell('trans_dates', 'reciprocal_pe_ttm', 'all_reciprocal_pe_ttm', 'Reciprocal of PE_ttm', 'M', baseDayStr, currDayStr, 0)